package name.jautzy.esi.server;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;

import com.googlecode.objectify.helper.DAOBase;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.ObjectifyService;
import com.googlecode.objectify.Query;

import name.jautzy.cedb.model.*;
import name.jautzy.esi.client.model.EsiShipFilter;
import name.jautzy.esi.client.model.Fit;
import name.jautzy.esi.client.model.EsiUser;
import name.jautzy.esi.server.model.FitComment;
import name.jautzy.esi.server.model.FitFactory;
import name.jautzy.esi.server.model.FitProxy;
import net.sf.jsr107cache.Cache;
import net.sf.jsr107cache.CacheFactory;
import net.sf.jsr107cache.CacheManager;

public class EdbDAO extends DAOBase {
	static {
        ObjectifyService.register(Attribute.class);
        ObjectifyService.register(Category.class);
        ObjectifyService.register(Group.class);
        ObjectifyService.register(Equipment.class);
        ObjectifyService.register(Effect.class);
        ObjectifyService.register(Bonus.class);
        ObjectifyService.register(Fit.class);
        ObjectifyService.register(FitProxy.class);
        ObjectifyService.register(EsiUser.class);
        ObjectifyService.register(FitComment.class);

        ObjectifyService.setDatastoreTimeoutRetryCount(3);
    }
	
	private static Cache attCache = null;
	private static Cache eqCache = null;
	
	private static Cache getAttCache() {
		try {
			if (attCache ==null) {
				CacheFactory cacheFactory = CacheManager.getInstance().getCacheFactory();
				attCache = cacheFactory.createCache(Collections.emptyMap());
			}
		} catch (Exception e) {
			
		}
		return attCache;
	}
	
	private static Cache getEqCache() {
		try {
			if (eqCache ==null) {
				CacheFactory cacheFactory = CacheManager.getInstance().getCacheFactory();
				eqCache = cacheFactory.createCache(Collections.emptyMap());
			}
		} catch (Exception e) {
			
		}
		return eqCache;
	}
	
	public EdbDAO() {
		super();
	}
	
	public Equipment getEquipmentFor(String name) {	
		Cache cache = getEqCache();
		if (cache.containsKey(name)) 
			return (Equipment)cache.get(name);
		Query<Equipment> query = ofy().query(Equipment.class).filter("name",name);		
		Equipment equip = query.get();
		cache.put(name,equip);
		return equip;
	}
	
	public HashSet<String> getEffectsNameAsSetFor(Equipment equip) {
		HashSet<String> retSet = new HashSet<String>();
		
		for (Effect eff : getEffectsFor(equip))
			retSet.add(eff.getName());
		
		return retSet;
	}
	
	public Iterable<Effect> getEffectsFor(Equipment equip) {
		return getEffects(equip.getEqId());
	}
	
	public Group getGroupFor(Equipment equip) {
		Query<Group> query = ofy().query(Group.class).filter("groupId",equip.getGroupId());		
		return query.get();
	}
	
	public Category getCategoryFor(Group group) {
		Query<Category> query = ofy().query(Category.class).filter("catId",group.getCatId());
		return query.get();
	}
	
	public Category getCategoryFor(Equipment equip) {		
		return getCategoryFor(getGroupFor(equip));
	}
	
	public Iterable<Attribute> getAttributesFor(Equipment equip) {
		Query<Attribute> query = ofy().query(Attribute.class).filter("eqId",equip.getEqId());
		return query.fetch();
	}
	
	public HashMap<String,Double> getAttributesAsMapFor(Equipment equip) {
		HashMap<String,Double> map; 
		Cache cache = getAttCache();
		long id = equip.getEqId();
		if (cache.containsKey(id))
			return (HashMap<String,Double>)cache.get(id);
			
		map = new HashMap<String,Double>();
		for (Attribute att : getAttributesFor(equip)) 
			map.put(att.getName(), att.getValue());
		
		cache.put(id, map);
		return map;
	}

	public Iterable<Category> getCategories() {
		Query<Category> query = ofy().query(Category.class);
		return query.fetch();	
	}
	
	public Iterable<Key<Category>> getCategoryKeys() {
		Query<Category> query = ofy().query(Category.class);
		return query.fetchKeys();	
	}
	
	public Iterable<Group> getGroups(long catId) {
		Query<Group> query = ofy().query(Group.class).filter("catId",catId);
		return query.fetch();		
	}
	
	public Iterable<Key<Group>> getGroupKeys() {
		Query<Group> query = ofy().query(Group.class);
		return query.fetchKeys();		
	}
	
	public Iterable<Group> getShipGroups() {
		return getGroups(6);
	}

	public Iterable<Equipment> getEquipments(long groupId) {
		Query<Equipment> query = ofy().query(Equipment.class).filter("groupId",groupId);
		return query.fetch();
	}
	
	public Iterable<Key<Equipment>> getEquipmentKeys() {
		Query<Equipment> query = ofy().query(Equipment.class);
		return query.fetchKeys();
	}
	
	public Iterable<Attribute> getAttributes(long eqId) {
		Query<Attribute> query = ofy().query(Attribute.class).filter("eqId",eqId);
		return query.fetch();
	}
	
	public Iterable<Key<Attribute>> getAttributeKeys() {
		Query<Attribute> query = ofy().query(Attribute.class);
		return query.fetchKeys();
	}

	public Iterable<Effect> getEffects(long eqId) {
		Query<Effect> query = ofy().query(Effect.class).filter("eqId",eqId);
		return query.fetch();
	}
	
	public Iterable<Key<Effect>> getEffectKeys() {
		Query<Effect> query = ofy().query(Effect.class);
		return query.fetchKeys();
	}
	
	public Iterable<Bonus> getBonus(String shipName) {
		Query<Bonus> query = ofy().query(Bonus.class).filter("shipName",shipName);
		return query.fetch();
	}
	
	public Iterable<Key<Bonus>> getBonusKeys() {
		Query<Bonus> query = ofy().query(Bonus.class);
		return query.fetchKeys();
	}
	
	public HashMap<String,Double> getBonusAsMapFor(Equipment equip) {
		HashMap<String,Double> map = new HashMap<String,Double>();
		for (Bonus att : getBonus(equip.getName())) 
			map.put(att.getBonusName(), att.getValue());
		return map;
	}
	
	public Bonus getBonus(long bonusId) {
		try {
			return ofy().get(Bonus.class,bonusId);
		}
		catch (Exception e) {
			return null;
		}
	}
	
	public void putBonus(Bonus b) {
		ofy().put(b);
	}
	
	public void removeBonus(Bonus b) {
		ofy().delete(b);
	}
	
	public void putFit(FitFactory fact) {
		   ofy().put(fact.getFitProxy());
		   ofy().put(fact.getFit());
	}
	
	public void putFit(Fit fit) {
		   ofy().put(fit);
	}
	
	public boolean addFitUser (String name, String fitId) {
		if (!fitExist(fitId))
			return false;
		if (fitProxyExist(name, fitId))
			return false;
		Query<FitProxy> query = ofy().query(FitProxy.class);
		query.filter("fitId", fitId);
		query.filter("copy", false);
		FitProxy fit = query.fetch().iterator().next();
		fit.incScore();
		ofy().put(fit.copyFor(name));
		ofy().put(fit);
		return true; 
	}
	
	public Iterable<FitProxy> getFits () {
		return ofy().query(FitProxy.class).fetch();
	}
	
	public Iterable<FitProxy> getFits(EsiShipFilter filter) {
			Query<FitProxy> query = ofy().query(FitProxy.class);
			String user = filter.getUser();
			String groupName = filter.getGroupName();
			String race = filter.getRace();
		
			if (user!=null) 
				query.filter("maker", user);
			else
				query.filter("copy", false);
			if (groupName!=null)
				query.filter("groupName", groupName);
			if (race!=null)
				query.filter("race", race);
			query.order("-score");
			query.limit(filter.getLimit());
			query.offset(filter.getOffset());

			filter.setCountResult(query.countAll());
		
			return query.fetch();
	}
	
	public Fit getFit(String id) {
		try {
			return ofy().get(Fit.class,id);
		}
		catch (Exception e) {
			return null;
		}
	}
	
	public FitProxy getFitProxy(String fitId) {
		try {
			Query<FitProxy> query = ofy().query(FitProxy.class);
			query.filter("fitId", fitId);
			query.filter("copy", false);
			return query.get();
		}
		catch (Exception e) {
			return null;
		}
	}
	
	public void removeFits(List<String> ids, String user) {
		for (String id : ids) {
			Query<FitProxy> query = ofy().query(FitProxy.class);
			query.filter("fitId", id);
			if (query.countAll()==1) { // This is the last reference
				ofy().delete(new Key<Fit>(Fit.class,id));
				ofy().delete(query.getKey());
			}
			else { // only delete the right proxy
				query = ofy().query(FitProxy.class);
				query.filter("maker", user);
				query.filter("fitId", id);
				ofy().delete(query.fetchKeys());
			}		
		}
	}
	
	public void removeEquip(List<Long> ids) {
		List<Key<Equipment>> eqList = new LinkedList<Key<Equipment>>();
		for (Long id : ids) {
			eqList.add(new Key<Equipment>(Equipment.class,id));
			Query<Attribute> query = ofy().query(Attribute.class);
			query.filter("eqId", id);
			ofy().delete(query.fetchKeys());
		}
		ofy().delete(eqList);
	}
	
	public boolean fitExist(String id) {
		if (ofy().find(Fit.class,id)==null)
			return false;
		return true;
	}
	
	public boolean fitProxyExist(String name, String fitId) {
		Query<FitProxy> query = ofy().query(FitProxy.class);
		query.filter("maker", name);
		query.filter("fitId", fitId);
		if (query.countAll()==0)
			return false;
		return true;
	}
	
	public void putUser(EsiUser user) {
		ofy().put(user);
	}
	
	public EsiUser getUser(String id) {
		return ofy().find(EsiUser.class, id);
	}
	
	public Iterable<FitComment> getComments (String fitId) {
		Query<FitComment> query = ofy().query(FitComment.class);
		query.filter("fitId", fitId);
		return query.fetch();
	}
	
	public void putComment(FitComment comment) {
		FitProxy proxy = getFitProxy(comment.getFitId());
		if (proxy != null && !comment.getMaker().equals(proxy.getMaker())) {
			proxy.incScore();
			ofy().put(proxy);
		}
		ofy().put(comment);
	}
}
